package de.gwtprime.server.model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import de.gwtprime.server.sql.helper.HibernateHelper;
import de.gwtprime.shared.VoteType;
import edu.emory.mathcs.backport.java.util.Collections;

@Entity
@Table(name = "article_section")
public class ArticleSection extends EntityBaseVoteable{

	/**
	 * SerialVersionUID
	 */
	private static final long serialVersionUID = 1L;
	
	private String title;

	private String text;
	
	private String images;
	
	private Set<Vote> votes = new HashSet<Vote>();
	
	private Set<VoteSectionComplete> voteSectionComplete = new HashSet<VoteSectionComplete>();

	private Set<Question> questions = new HashSet<Question>();
	
	/**
	 * Articles are only shown if they are published -> 
	 * They can be edited from admins while they are not published
	 */
	private boolean published = false;
	
	private int numberQuestions;
	/**
	 * Empty constructor for JPA
	 */
	public ArticleSection() {
	}

	public ArticleSection(String title, String text) {
		this.title = title;
		this.text = text;
	}

	/**
	 * @return the title
	 */
	public String getTitle() {
		return title;
	}

	/**
	 * @param title
	 *            the title to set
	 */
	public void setTitle(String title) {
		this.title = title;
	}

	/**
	 * @return the text
	 */
	@Lob
	public String getText() {
		return text;
	}

	/**
	 * @param text
	 *            the text to set
	 */
	public void setText(String text) {
		this.text = text;
	}

	private Article article;

	public static ArticleSection findArticleSection(Long id) {
		return HibernateHelper.find(ArticleSection.class, id);
	}

	@ManyToOne
	@JoinColumn
	public Article getArticle() {
		return article;
	}

	public void setArticle(Article article) {
		this.article = article;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "ArticleSection [title=" + title + ", text=" + text
				+ ", getId()=" + getId() + "]";
	}

	public void addVote(Vote vote){
		EntityManager em = HibernateHelper.beginTransactionWithoutTransaction();

		try{
			User2 user = vote.getUser();

			if(user==null){
				// Not logged in users can't vote
				return;
			}

			if(getId()==null) persistPure(em);



			Vote voteFromDB = HibernateHelper.queryDB(new Vote(), String.format("from %s WHERE articleSection.id=%s AND user.id =%s", Vote.class.getSimpleName(), getId(), user.getId()));
			// if the user already votes -> just update the existing vote
			// with the new type (UP, DOWN)
			if(voteFromDB!=null){

				updateExistingVote(vote, em, voteFromDB);
				return;
			}else{
				vote.setUser((User2)user.mergePure(em));
				vote.setArticleSection(this);
				vote.persistPure(em);
			}


			if(votes.contains(vote)) return;
			votes.add(vote);

			if(vote.getType()==VoteType.DOWN)
				voteValue--;
			else if(vote.getType()==VoteType.UP)
				voteValue++;

		}finally{
			em.close();

		}
	}

	private void updateExistingVote(Vote vote, EntityManager em, Vote voteFromDB) {
		// if same type -> no update needed
		if(voteFromDB.getType()==vote.getType()){
			return;
		}

		// if vote is applied in opposite direction -> increase/decrease by 2 
		if(vote.getType()==VoteType.UP){
			voteValue = voteValue + 2;
		}else if(vote.getType()==VoteType.DOWN){
			voteValue = voteValue - 2;
		}
		voteFromDB.setType(vote.getType());
		voteFromDB.mergePure(em);
		return;
	}
	
	public void addVoteAndPersist(Vote vote, User2 user){
		vote.setUser(user);
		addVote(vote);
		persist();
	}
	
	
	@OneToMany(mappedBy = "articleSection", cascade = CascadeType.REMOVE)
	public Set<Vote> getVotes() {
		return votes;
	}

	public void setVotes(Set<Vote> votes) {
		this.votes = votes;
	}

	public int getVoteValue() {
		return voteValue;
	}

	public void setVoteValue(int voteValue) {
		this.voteValue = voteValue;
	}
	
	
	
	@Override
	public void decreaseVoteValueAndPersist(){
		decreaseVoteValue();
		persist();
	}

	@OneToMany(mappedBy = "articleSection", cascade = CascadeType.ALL)
	public Set<Question> getQuestion() {
		return questions;
	}
	
	public void setQuestion(Set<Question> question) {
		this.questions = question;
	}
	
	public Question addQuestion(Question question){

		if(getId()==null) persist(); 

		question.setArticleSection(this);
		question = HibernateHelper.merge(question);

		if(questions.contains(question)) return question;
		questions.add(question);
		setNumberQuestions(getNumberQuestions() + 1); 
		persist();

		return question;
	}

	public int getNumberQuestions() {
		return numberQuestions;
	}

	public void setNumberQuestions(int numberQuestions) {
		this.numberQuestions = numberQuestions;
	}
	
	public List<Question> loadQuestions(){
		
		// TODO more performant way?
		List<Question> questions = new ArrayList<Question>(getQuestion());
		Collections.sort(questions);
		
		return questions;
	}

	@OneToMany(mappedBy = "articleSection")
	public Set<VoteSectionComplete> getVoteSectionComplete() {
		return voteSectionComplete;
	}

	public void setVoteSectionComplete(Set<VoteSectionComplete> voteSectionComplete) {
		this.voteSectionComplete = voteSectionComplete;
	}
	
	public void addVoteSectionComplete(VoteSectionComplete vote){
		EntityManager em = HibernateHelper.beginTransactionWithoutTransaction();

		User2 user = vote.getUser();

		if(user==null){
			// Not logged in users can't vote
			return;
		}

		if(getId()==null) persistPure(em);

		VoteSectionComplete voteFromDB = HibernateHelper.queryDB(new VoteSectionComplete(), String.format("from %s WHERE articleSection.id=%s AND user.id =%s", VoteSectionComplete.class.getSimpleName(), getId(), user.getId()));
		// if the user already votes -> just update the existing vote
		// with the new type (UP, DOWN)
		
		//Only one vote allowed
		if(voteFromDB!=null){
			return;
			
		}else{
			vote.setUser((User2)user.mergePure(em));
			vote.setArticleSection(this);
			vote.persistPure(em);
		}
		
		if(voteSectionComplete.contains(vote)) return;
		voteSectionComplete.add(vote);
		
		em.close();
	}
	
	public void addVoteSectionCompleteAndPersist(VoteSectionComplete vote, User2 user){
		vote.setUser(user);
		addVoteSectionComplete(vote);
		persist();
	}

	@Lob
	public String getImages() {
		return images;
	}

	public void setImages(String images) {
		this.images = images;
	}

	public boolean isPublished() {
		return published;
	}

	public void setPublished(boolean published) {
		this.published = published;
	}
	
}
